package main

import (
	"fmt"
	"io"
	"os"
	"strings"
)

type SAMessageBed struct {
	writer     io.Writer
	ErrorCount int
	WarniCount int
}

func NewSAMessageBed(writer io.Writer) *SAMessageBed {
	return &SAMessageBed{
		writer:     writer,
		ErrorCount: 0,
		WarniCount: 0,
	}
}

func (r *SAMessageBed) Errorf(f string, args ...any) {
	ASSERT(r.writer != nil)
	r.ErrorCount++
	fmt.Fprintf(r.writer, "Error: ")
	fmt.Fprintf(r.writer, f, args...)
	fmt.Fprintf(r.writer, "\n")
}

func (r *SAMessageBed) Error(t string) {
	ASSERT(r.writer != nil)
	r.ErrorCount++
	fmt.Fprintf(r.writer, "Error: ")
	fmt.Fprintf(r.writer, "%v", t)
	fmt.Fprintf(r.writer, "\n")
}

func (r *SAMessageBed) Warningf(f string, args ...any) {
	ASSERT(r.writer != nil)
	r.WarniCount++
	fmt.Fprintf(r.writer, "Warning: ")
	fmt.Fprintf(r.writer, f, args...)
	fmt.Fprintf(r.writer, "\n")
}

func (r *SAMessageBed) Warning(t string) {
	ASSERT(r.writer != nil)
	r.WarniCount++
	fmt.Fprintf(r.writer, "Warning: ")
	fmt.Fprintf(r.writer, "%v", t)
	fmt.Fprintf(r.writer, "\n")
}

type SADocumentDiffer struct {
	TargetDoc    *DatasetDocument
	SourceDoc    *DatasetDocument
	MessagBed    *SAMessageBed
	contextLayer []string
}

func NewSADocumentDiffer(TargetDoc *DatasetDocument, SourceDoc *DatasetDocument, MessagBed *SAMessageBed) *SADocumentDiffer {
	return &SADocumentDiffer{
		TargetDoc:    TargetDoc,
		SourceDoc:    SourceDoc,
		MessagBed:    MessagBed,
		contextLayer: make([]string, 0, 5),
	}
}

func (d *SADocumentDiffer) Context() string {
	return strings.Join(d.contextLayer, ".")
}

func (d *SADocumentDiffer) Diff() int {

	d.DiffWith("HEADER", d.DiffHeaderItems)
	d.DiffWith("TYPE", d.DiffTypeItems)
	d.DiffWith("TRACESWEEP", d.DiffSignalItems)
	d.DiffWith("HEADER", d.DiffSequenceItems)

	return d.MessagBed.ErrorCount
}

func (d *SADocumentDiffer) DiffWith(ctxText string, diff func()) {
	d.contextLayer = append(d.contextLayer, ctxText)
	diff()
	d.contextLayer = d.contextLayer[:len(d.contextLayer)-1]
}

func (d *SADocumentDiffer) DiffValue(targetValue Value, sourceValue Value) {
}

func (d *SADocumentDiffer) DiffProperty(targetProperty *Property, sourceProperty *Property) {
	if targetProperty.Name != sourceProperty.Name {
		d.MessagBed.Errorf("[%v.%v] 未在 %s 文件中定义", d.Context(), targetProperty.Name, d.SourceDoc.FileName)
	}

	d.DiffWith("$$", func() {
		d.DiffValue(targetProperty.Value, sourceProperty.Value)
	})
}

func (d *SADocumentDiffer) DiffHeaderItems() {
	// 所有的 target 应该在 source 里
	for targetElem := d.TargetDoc.HeaderItems.Front(); targetElem != nil; targetElem = targetElem.Next() {
		targetProperty := targetElem.Value.(*Property)
		sourceElem, ok := d.SourceDoc.HeaderItems.Find(targetProperty.Name)
		if !ok {
			d.MessagBed.Errorf("[%v.%v] 未在 %s 文件中定义", d.Context(), targetProperty.Name, d.SourceDoc.FileName)
			continue
		}
		sourceProperty := sourceElem.Value.(*Property)
		d.DiffWith(targetProperty.Name, func() {
			d.DiffProperty(targetProperty, sourceProperty)
		})
	}

	// 所有的 source 应该在 target 里
	for sourceElem := d.SourceDoc.HeaderItems.Front(); sourceElem != nil; sourceElem = sourceElem.Next() {
		sourceProperty := sourceElem.Value.(*Property)
		_, ok := d.TargetDoc.HeaderItems.Find(sourceProperty.Name)
		if !ok {
			d.MessagBed.Errorf("[%v.%v] 未在 %s 文件中定义", d.Context(), sourceProperty.Name, d.TargetDoc.FileName)
			continue
		}
	}
}

func (d *SADocumentDiffer) DiffTypeItems() {

}

func (d *SADocumentDiffer) DiffSignalItems() {

}

func (d *SADocumentDiffer) DiffSequenceItems() {
}

// cmd_diff 用于直接对比两个文件
//
//	参数： diff TargetFile BaselineFile
func cmd_diff(argv []string) int {
	ASSERT(len(argv) >= 4)

	targetFile := argv[1]
	baselineFile := argv[2]

	// 创建一个对比目标文档
	targetDoc := NewSAReader().Read(targetFile).(*DatasetDocument)

	// 创建一个对比基线文档
	baselineDoc := NewSAReader().Read(baselineFile).(*DatasetDocument)

	// 创建一个消息床
	messageBed := NewSAMessageBed(os.Stdout)

	// 构造一个differ
	ctx := NewSADocumentDiffer(targetDoc, baselineDoc, messageBed)

	// 对比
	ctx.Diff()

	return messageBed.ErrorCount + messageBed.WarniCount
}
